package com.zy.cat.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.zy.cat.common.utils.TJKUtils;
import com.zy.cat.entity.app.CatAppNewDeviceInfo;
import com.zy.cat.entity.app.CatAppNotice;
import com.zy.cat.entity.app.CatAppPvMenus;
import com.zy.cat.entity.app.CatDevUserUV;
import com.zy.cat.entity.app.CatEventElement;
import com.zy.cat.entity.app.repository.CatAppNewDeviceInfoRepository;
import com.zy.cat.entity.app.repository.CatAppNoticeRepository;
import com.zy.cat.entity.app.repository.CatAppPvMenusRepository;
import com.zy.cat.entity.app.repository.CatDevUserUVRepository;
import com.zy.cat.entity.app.resp.CatAppNoticeResp;
import com.zy.cat.entity.app.resp.CatEventElementResp;
import com.zy.cat.service.CacheService;
import com.zy.cat.service.CatAppService;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @info 
 * @author Link
 * @date 2019-12-09 14:03
 */
@Slf4j
@Service
public class CatAppServiceImpl implements CatAppService {
	
	@Autowired
	CacheService cacheService;
	
	@Autowired
	@Qualifier("redis14")
	RedisTemplate<String, String> redis14;
	
	@Autowired
	@Qualifier("redis15")
	RedisTemplate<String, Object> redis15;
	
	@Autowired
	CatAppNewDeviceInfoRepository catAppNewDeviceInfoRepository;
	
	@Autowired
	CatDevUserUVRepository catDevUserUVRepository;
	
	@Autowired
	CatAppPvMenusRepository catAppPvMenusRepository;
	
	@Autowired
	CatAppNoticeRepository catAppNoticeRepository;
	
	/* 所有界面每日PV统计 */
	private final String PV_ALL_KEY="PV_CAT_APP_ALL";
	/* 安卓界面每日PV统计 */
	private final String PV_AN_KEY="PV_CAT_APP_AN";
	/* IOS界面每日PV统计 */
	private final String PV_IOS_KEY="PV_CAT_APP_IOS";
	
	
	/* 每日新增统计 */
	private final String UV_ALL_NEW_KEY="UV_CAT_APP_ALL_NEW";
	/* 每日安卓新增统计 */
	private final String UV_AN_NEW_KEY="UV_CAT_APP_AN_NEW";
	/* 每日IOS新增统计 */
	private final String UV_IOS_NEW_KEY="UV_CAT_APP_IOS_NEW";
	
	/* 每日日活统计 */
	private final String UV_ALL_ACT_KEY="UV_CAT_APP_ALL_ACT";
	/* 每日安卓日活统计 */
	private final String UV_AN_ACT_KEY="UV_CAT_APP_AN_ACT";
	/* 每日IOS日活统计 */
	private final String UV_IOS_ACT_KEY="UV_CAT_APP_IOS_ACT";
	
	/* 每日事件统计 */
	private final String EVENT_STATISTIC_KEY="EVENT_STATISTIC";
	
	private final String USER_OPEN_NUM = "USER_OPEN_NUM";
	
	@Override
	@Async("zyasyncPoolTaskExecutor")
	public void insertPV(int rtype, String pid, String brand, String model, String vscode, String cid, String imei, String imsi, String mid, String cmid) {
		String ntime=LocalDate.now().toString();
		Map<String, List<String>> map=new HashMap<String, List<String>>();
		/* 根据渠道号cid，分类统计 */
		String CID_MID_CMID="CID_"+cid+"_MID_"+mid+"_CMID_"+cmid;
		
		/* 根据版本号vscode，分类统计 */
		String VS_MID_CMID="VS_"+vscode+"_MID_"+mid+"_CMID_"+cmid;
		
		/* 根据菜单MID，分类统计 */
		String MID_CMID="MID_"+mid+"_CMID_"+cmid;
		
		/*根据用户PID，分类统计 设置hashkey： 渠道号_版本号_菜单mid_子菜单ID_pid 例: ym-xiaomi_106_12_24_2252133322 */
		String hashKey=cid+"_"+vscode+"_"+mid+"_"+cmid+"_"+pid;
		
		String[]  items= {CID_MID_CMID,VS_MID_CMID,MID_CMID,hashKey};
//		String[]  items= {hashKey};
		 
		List<String> list=Arrays.asList(items);
		if(rtype==0) {
			map.put(PV_AN_KEY+"_"+ntime, list);
		}else if(rtype==1) {
			map.put(PV_IOS_KEY+"_"+ntime, list);
		}
		map.put(PV_ALL_KEY+"_"+ntime, list);
		
		/* 根据渠道号cid，分类统计 */
		String CID="CID_"+cid;
		/* 根据版本号vscode，分类统计 */
		String VS="VS_"+vscode;
		/* 今日总计 */
		String ALL="ALL";
		String cidkey="",vskey="";
		if(rtype==0) {
			cidkey="AN"+CID;
			vskey="AN"+VS;
		}else if(rtype==1) {
			cidkey="IOS"+CID;
			vskey="IOS"+VS;
		}
		
		if(cidkey!=""&&!redis14.opsForHash().hasKey("CAT_APP_CIDS", cidkey)) {
			redis14.opsForHash().put("CAT_APP_CIDS", cidkey, cid);
		}
		if(vskey!=""&&!redis14.opsForHash().hasKey("CAT_APP_VSS", vskey)) {
			redis14.opsForHash().put("CAT_APP_VSS", vskey, vscode);
		}
		
		if(!map.isEmpty()) {
			redis14.executePipelined(new RedisCallback<Long>() {
	            public Long doInRedis(RedisConnection connection) throws DataAccessException {
	                connection.openPipeline();
	                map.forEach((key,hkeys)->{
	                	hkeys.forEach(hkey->{
		                	connection.hIncrBy(key.getBytes(), hkey.getBytes(), 1);	
	                	});
	                	connection.expire(key.getBytes(), 60*60*24*7);
	                });
	                return null;
	            }
	        });
		}
	}

	@Override
	@Async("zyasyncPoolTaskExecutor")
	public void insertUV(int rtype, String pid, String brand, String model, String vscode, String cid, String imei, String imsi) {
		Map<String, List<String>> map=new HashMap<String, List<String>>();
		
		String ntime=LocalDate.now().toString();
		
		 /* 新增，redis统计，mongodb存储,task统计昨日新增到数据库 */
		Boolean isNewDrivice=cacheService.IsNewDevice(rtype+"_"+imei );
		if(isNewDrivice==null) {
			if(catAppNewDeviceInfoRepository.save(new CatAppNewDeviceInfo(rtype, brand, model, vscode, cid, imei, imsi, TJKUtils.DateTime()))!=null) {
				/* 根据渠道号cid，分类统计 */
				String CID="CID_"+cid;
				
				/* 根据版本号vscode，分类统计 */
				String VS="VS_"+vscode;
				
				/* 今日总计 */
				String ALL="ALL";
				
				
				String[] items= {CID,VS,ALL};
				 
				List<String> list=Arrays.asList(items);
				if(rtype==0) {
					map.put(UV_AN_NEW_KEY+"_"+ntime, list);
				}else if(rtype==1) {
					map.put(UV_IOS_NEW_KEY+"_"+ntime, list);
				}
				map.put(UV_ALL_NEW_KEY+"_"+ntime, list);
				
				String cidkey="",vskey="";
				if(rtype==0) {
					cidkey="AN"+CID;
					vskey="AN"+VS;
				}else if(rtype==1) {
					cidkey="IOS"+CID;
					vskey="IOS"+VS;
				}
				if(cidkey!=""&&!redis14.opsForHash().hasKey("CAT_APP_CIDS", cidkey)) {
					redis14.opsForHash().put("CAT_APP_CIDS", cidkey, cid);
				}
				if(vskey!=""&&!redis14.opsForHash().hasKey("CAT_APP_VSS", vskey)) {
					redis14.opsForHash().put("CAT_APP_VSS", vskey, vscode);
				}
			}
		}
		/* 日活 ,判断今日是否统计，task统计昨日日活*/
		Boolean isActiveDrivice=cacheService.IsActivedDevice(rtype+"_"+imei+"_"+pid+"_"+ntime);
		if(isActiveDrivice==null) {
			//HashKey格式: 渠道_版本(_pid),例:ym-xiaomi_105_79612252,或ym-xiaomi_105
			/* 根据渠道号cid，分类统计 */
            String CID="CID_"+cid;
            
            /* 根据版本号vscode，分类统计 */
            String VS="VS_"+vscode;
            
            /*根据用户ID统计*/
			String hKey=cid+"_"+vscode;
			if (!StringUtils.isEmpty(pid)) {
				hKey+="_"+pid;
			}
			
			/* 今日总计 */
			String ALL="ALL";
			
			String[]  items= {CID,VS,hKey,ALL};
			 
			List<String> list=Arrays.asList(items);
			if(rtype==0) {
				map.put(UV_AN_ACT_KEY+"_"+ntime, list);
			}else if(rtype==1) {
				map.put(UV_IOS_ACT_KEY+"_"+ntime, list);
			}
			map.put(UV_ALL_ACT_KEY+"_"+ntime, list);
		}
		
		/* 30日留存,新登录的用户，记录登录历史作为30日留存统计条件信息 ,task统计信息到数据库*/
		if(isActiveDrivice==null) {
			List<CatDevUserUV> catUVs=catDevUserUVRepository.findByImeiOrderByLtimeDesc(imei);
			CatDevUserUV catUV=new CatDevUserUV(imei, rtype, cid, vscode, ntime, ntime);
			if(catUVs!=null&&!catUVs.isEmpty()) {
				catUV.setCtime(catUVs.get(0).getCtime());
			}
			catDevUserUVRepository.save(catUV);
		}
		
		/* 存储信息到redis */
		if(!map.isEmpty()) {
			redis14.executePipelined(new RedisCallback<Long>() {
	            public Long doInRedis(RedisConnection connection) throws DataAccessException {
	                connection.openPipeline();
	                map.forEach((key,hkeys)->{
	                	hkeys.forEach(hkey->{
		                	connection.hIncrBy(key.getBytes(), hkey.getBytes(), 1);	
	                	});
	                	connection.expire(key.getBytes(), 60*60*24*7);
	                });
	                return null;
	            }
	        });
		}
	}

	@Override
	public List<CatAppPvMenus> getAppPVMenus(int rtype, String brand, String model, String vscode, String cid,
			String imei, String imsi, String mid, String cmid) {
		List<CatAppPvMenus> result = new ArrayList<CatAppPvMenus>();
		if (StringUtils.isBlank(mid) || "0".equals(mid)) {
			result = catAppPvMenusRepository.findAllByOrderByMidAsc();
		} else {
			if (StringUtils.isBlank(cmid) || "0".equals(cmid)) {
				result = catAppPvMenusRepository.findByMid(mid);
			} else {
				List<CatAppPvMenus> resultTemp = catAppPvMenusRepository.findByMid(mid);
				if(resultTemp!=null&&!resultTemp.isEmpty()) {
					return getAppPVMenu(cmid, resultTemp.get(0).getChilds());
				}
			}
		}
		if (result == null || result.isEmpty()) {
			return null;
		}
		return result;
	}
	
	private List<CatAppPvMenus> getAppPVMenu(String cmid, List<CatAppPvMenus> child) {
		List<CatAppPvMenus> result=new ArrayList<CatAppPvMenus>();
		child.forEach(cpvm->{
			if(cmid.equals(cpvm.getMid())) {
				result.add(cpvm);
				return;
			}else if(cmid.contains(cpvm.getMid())) {
				result.addAll(getAppPVMenu(cmid, cpvm.getChilds()));
			}
		});
		return result;
	}

	@Override
	public CatAppNoticeResp getMessageInfo(Integer rtype, String pid) {
		List<CatAppNotice> list = catAppNoticeRepository.findByMstatus(1);
		if (list==null || list.size()<1) {
			return null;
		}
		//从集合中随机抽取
		Random random=new Random();
		CatAppNoticeResp resp=new CatAppNoticeResp();
		BeanUtils.copyProperties(list.get(random.nextInt(list.size())), resp);
		return resp;
	}

	@Override
	public void setEventCount(Integer rtype,Integer appId, String pid, String brand, String model, String vscode, String cid,
			String imei, String imsi, String eventData) {
		//校验事件ID是否有效
		List<CatEventElement> elementList = cacheService.getEventElementList();
		if (elementList==null || elementList.size()==0) {
			log.info("不存在统计事件");
			return;
		}
		Set<Integer> eventSet = elementList.stream().map(CatEventElement::getId).collect(Collectors.toSet());
		String[] split = eventData.split(",");
		List<String> collect = Arrays.stream(split).filter(str->!eventSet.contains(str)).collect(Collectors.toList());
		
		//将事件记录到缓存中
		String ntime=LocalDate.now().toString();
		String key=EVENT_STATISTIC_KEY+"_"+ntime;
		
		String param="version_%s_clientType_%s_appId_%s_userType_%s_wdLevel_%s_orderLevel_%s_eventId_%s";
		
		//默认-1、未登录用户
		int userType=-1;
		
		//获取用户类型，-1、未登录用户、0.新用户、1.老用户
		if (!StringUtils.isEmpty(pid)) {
			// 获取用户打开应用次数
			Integer openNum = (Integer) redis15.opsForHash().get(USER_OPEN_NUM, pid);
			//启动次数大于四次认为是老用户
			userType=openNum==null || openNum<5?0:1;
		}
		
		//提现级别,0.零次提现、1.有一次提现、2.两次及以上提现
		int remitLevel=0;
		Integer count = cacheService.getRemitCount(pid);
		if (count!=null) {
			remitLevel=count>2?2:count;	
		}
		
		//订单数级别，0.未下过订单、1.下过一次订单、2.两次、3.三次订单及以上
		int orderLevel=0;
		Integer orderNum = cacheService.getOrderNumber(pid,pid, "");
		if (orderNum!=null) {
			orderLevel=orderNum>=3?3:orderNum;
		}
		
		List<String> hashKeyList=new ArrayList<>();
		
		for (String eventId : collect) {
			hashKeyList.add(String.format(param,vscode,rtype,appId,userType,remitLevel,orderLevel,eventId));
		}		
		
		redis14.executePipelined(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline();
                hashKeyList.forEach((hkeys)->{
                	connection.hIncrBy(key.getBytes(), hkeys.getBytes(), 1);
                	connection.expire(key.getBytes(), 60*60*24*7);
                });
                return null;
            }
        });
	}

	@Override
	public List<CatEventElementResp> getEventElementList(Integer rtype, String vscode, String cid, String imei, String imsi) {
		List<CatEventElement> elementList = cacheService.getEventElementList();
		List<CatEventElementResp> resp=JSON.parseArray(JSON.toJSONString(elementList), CatEventElementResp.class);	
//		BeanUtils.copyProperties(elementList,resp);
		return resp;
	}
}
